#!/usr/bin/env python3
# -*- coding=utf-8 -*-

_BIT        = lambda x: 1 << x
_MASK       = lambda h, l: (_BIT(h) - 1) & ~(_BIT(l) - 1)

A_OFF       = _BIT(0)       # 默认
A_BOLD      = _BIT(1)
A_DIM       = _BIT(2)       # 暗化
A_ITALICS   = _BIT(3)       # 斜体
A_UNDERLINE = _BIT(4)       # 下划线
A_BLINK     = _BIT(5)
A_REVERSE   = _BIT(7)
A_CONCEALED = _BIT(8)       # 不显示
A_DELETE    = _BIT(9)
A_MASK      = _MASK(10, 1)  # ignore A_OFF

F_BLACK     = _BIT(30)
F_RED       = _BIT(31)
F_GREEN     = _BIT(32)
F_YELLOW    = _BIT(33)
F_BLUE      = _BIT(34)
F_MAGENTA   = _BIT(35)      # 紫红
F_CYAN      = _BIT(36)      # 青蓝
F_WHITE     = _BIT(37)
F_MASK      = _MASK(38, 30)

B_BLACK     = _BIT(40)
B_RED       = _BIT(41)
B_GREEN     = _BIT(42)
B_YELLOW    = _BIT(43)
B_BLUE      = _BIT(44)
B_MAGENTA   = _BIT(45)
B_CYAN      = _BIT(46)
B_WHITE     = _BIT(47)
B_MASK      = _MASK(48, 40)


class Styler:
    _STYLE_HEAD = '\033['  # \033, Esc, can not use \e or r'\033['
    _STYLE_TAIL = 'm'      # can not use 'm\]'

    _STYLE_OFF = '\033[0m'

    def __init__(self, *args):
        self._style = self.STYLE(*args)

    def __call__(self, text, end_off=True):
        return self.decorate(text, end_off)

    @property
    def style(self):
        return self._style

    def decorate(self, text:str, end_off=True):
        text = self._style + text
        if end_off:
            text += self._STYLE_OFF
        return text

    @classmethod
    def decorate_style(cls, text, style:int=0, end_off=True):
        text = cls.STYLE(style) + text
        if end_off:
            text += cls._STYLE_OFF
        return text

    @classmethod
    def _STYLE(cls, style):
        # process 'A_BLINK | A_DIM | F_RED'
        sa, sf, sb = set(), set(), set()
        for n in range(0, 10):
            if style & _BIT(n) & A_MASK:
                sa.add(n)
            if style & _BIT(30 + n) & F_MASK:
                sf.add(30 + n)
            if style & _BIT(40 + n) & B_MASK:
                sb.add(40 + n)

        return sa, sf, sb

    @classmethod
    def _STR(cls, sa, sf, sb) -> str:
        style = cls._STYLE_OFF

        for n in range(0, 10):
            tmp = []
            if len(sa):
                tmp.append(str(sa.pop(0)))
            if len(sf):
                tmp.append(str(sf.pop(0)))
            if len(sb):
                tmp.append(str(sb.pop(0)))

            if tmp:
                style += cls._STYLE_HEAD
                style += ';'.join(tmp)
                style += cls._STYLE_TAIL

        return style

    @classmethod
    def STYLE(cls, *args) -> str:
        # restore to default style
        if len(args) == 0:
            return cls._STYLE_OFF

        sa, sf, sb = set(), set(), set()
        for n in args:
            _a, _f, _b = cls._STYLE(n)
            sa |= _a
            sf |= _f
            sb |= _b

        sa, sf, sb = sorted(sa), sorted(sf), sorted(sb)

        return cls._STR(sa, sf, sb)
